/*
<Copyright (C) 2011 Dru Moore>

This file is part of demorecorder.

vlc-remote is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

demorecorder is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with demorecorder.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "playpipeline.h"
#include <QDateTime>
#include <QDebug>

PlayPipeline::PlayPipeline(QObject *parent)
{
    Q_UNUSED(parent);
}

PlayPipeline::PlayPipeline(QString name, GstElement *src) : QObject() {
    Q_UNUSED(name);
    // this->name = name;
    this->src = src;
    this->position = 0;
    this->duration = 0;
    //define the queries
    duration_query = gst_query_new_duration(GST_FORMAT_TIME);
    position_query = gst_query_new_position(GST_FORMAT_TIME);
   // qDebug("pre construct");
    this->construct_pipeline();
   //qDebug("post construct");
}

PlayPipeline::PlayPipeline(QString name, GstElement *src, GstElement *sink, bool canLink) {
    this->src = src;
    this->sink = sink;
    this->position = 0;
    this->duration = 0;
    //define the queries
    duration_query = gst_query_new_duration(GST_FORMAT_TIME);
    position_query = gst_query_new_position(GST_FORMAT_TIME);
    qDebug("pre construct");
    this->pipeline = GST_PIPELINE(gst_pipeline_new(name.toLatin1()));
    qDebug("src");
    if (NULL != this->src) {
        gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->src));
    }
    qDebug("sink");
    if (NULL != this->sink) {
        gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->sink));
    }
    qDebug("linking?");
    if (NULL != this->src && NULL != this->sink) {
        if (canLink) {
            qDebug("can link");
            if (!gst_element_link(GST_ELEMENT(this->src), GST_ELEMENT(this->sink))) {
                qDebug("can't link actually!");
                 //failed!
            }
        }
        else {
           // qDebug("set up pad callback");
            // set up pad callback
            g_object_connect ((GObject*) this->src, "signal::pad-added", &PlayPipeline::pad_added, this, NULL);
            //g_object_connect ((GObject*) this->src, "swapped-object-signal::pad-added", &PlayPipeline::pad_added, this, NULL);
        }
    }
    qDebug("post construct");
    /*GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (this->pipeline));
    gst_bus_add_signal_watch(bus);
    g_object_connect((GObject*)bus, "message::eos", &PlayPipeline::bus_callback, this, NULL);
    g_object_connect((GObject*)bus, "message::error", &PlayPipeline::bus_callback, this, NULL);
    g_object_connect((GObject*)bus, "message::state-changed", &PlayPipeline::bus_callback, this, NULL);
    gst_bus_add_watch(bus, &PlayPipeline::bus_callback, this);
    guint watch_id = gst_bus_add_watch_full(bus, G_PRIORITY_DEFAULT, &PlayPipeline::bus_callback, gst_object_ref(this), gst_object_unref);*/
    //qDebug() << "added watch bus";

}

gboolean PlayPipeline::pad_added(GstElement* comp, GstPad* srcPad, PlayPipeline* data) {
    //qDebug("pad_added called");
    gboolean result = false;
    g_return_val_if_fail (comp != NULL, false);
    //qDebug("comp fine");
    g_return_val_if_fail (srcPad != NULL, false);
    //qDebug("srcPad fine");
    GstPadLinkReturn ret = gst_pad_link(srcPad, gst_element_get_compatible_pad(data->sink, srcPad, gst_pad_get_caps(srcPad)));
    //qDebug() << "return of pad_link: " << ret;
    result = true;
    return result;
}

bool PlayPipeline::set_src(GstElement *src) {
    gst_element_set_state(GST_ELEMENT(this->pipeline), (GST_STATE_NULL));
    if (NULL != this->src) {
        this->clear_src();
    }
    this->src = src;
    gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->src));
    gst_element_link(GST_ELEMENT(this->src), GST_ELEMENT(this->sink));
    return true;
}

void PlayPipeline::clear_src() {
    if (NULL != this->src) {
        gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_NULL);
        gst_element_unlink(GST_ELEMENT(this->src), GST_ELEMENT(this->sink));
        gst_bin_remove(GST_BIN(this), GST_ELEMENT(this->src));
        this->src = NULL;
    }
}

void PlayPipeline::construct_pipeline() {
    this->pipeline = GST_PIPELINE(gst_pipeline_new("pipeline"));
    if (NULL != this->src) {
        gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->src));
    }
    /*
    this->sink = gst_element_factory_make("pulsesink", "output");
    g_assert(this->sink);
    gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->sink));

    if (NULL != this->src) {
        if (!gst_element_link(GST_ELEMENT(this->src), GST_ELEMENT(this->sink))) {
             //failed!
        }
    }
    */


    /*GstBus *bus = gst_element_get_bus(GST_ELEMENT(this->src));
    gst_bus_add_signal_watch(bus);
    g_object_connect((GObject*)bus, "message::eos", &PlayPipeline::bus_callback, this, NULL);
    g_object_connect((GObject*)bus, "message::error", &PlayPipeline::bus_callback, this, NULL);
    g_object_connect((GObject*)bus, "message::state-changed", &PlayPipeline::bus_callback, this, NULL);

    guint watch_id = gst_bus_add_watch_full(bus, G_PRIORITY_DEFAULT, &PlayPipeline::bus_callback, gst_object_ref(this), gst_object_unref);*/

}

gboolean PlayPipeline::bus_callback(GstBus *bus, GstMessage *message, void* data) {
    //Q_UNUSED(bus);
    //Q_UNUSED(data);
    qDebug("bus_callback called");
    switch (GST_MESSAGE_TYPE(message)) {
        case GST_MESSAGE_EOS:
        qDebug("eos");
            ((PlayPipeline*)data)->end_of_stream();
            break;
        case GST_MESSAGE_ERROR:
        qDebug("error");
            break;
        case GST_MESSAGE_STATE_CHANGED:
        qDebug("state changed");
            break;
        default:
            break;
    }
    return true;
}

void PlayPipeline::get_duration_info() {
    //qDebug("get_duration_info requested");
    bool duration_result,position_result;
    duration_result = gst_element_query(GST_ELEMENT(this->pipeline), this->duration_query);
    position_result = gst_element_query(GST_ELEMENT(this->pipeline), this->position_query);
    if ( duration_result && position_result  ) {
        //qDebug("got a result");
        gint64 pos;
        gint64 dur;
        gst_query_parse_duration(this->duration_query, NULL, &dur);
        gst_query_parse_position(this->position_query, NULL, &pos);
        this->duration =(long long) dur;
        this->position =(long long) pos;
        emit this->position_duration(this->position, this->duration);
        if (this->position >= this->duration) {
            emit this->end_of_stream();
        }
    }
    else {
        //qDebug("no result. try again");
    }
}

long long PlayPipeline::get_current_time() {
    QDateTime setTime = QDateTime::fromString (QString("1970-07-18T14:15:09"), Qt::ISODate);
    QDateTime current = QDateTime::currentDateTime();
    return setTime.time().msecsTo(current.time());
}

void PlayPipeline::play() {
    //qDebug("pre play state");
    gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_PLAYING);
    //qDebug("post play state");
}

void PlayPipeline::pause() {
    gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_PAUSED);
}

void PlayPipeline::stop() {
    gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_READY);
}
